#pragma once
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include "Thread.hpp"
#include "common.hpp"
#include <vector>
#include <string>
#include <queue>
template<class T>
class ThreadPool
{
    ThreadPool(int n)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for(int i = 0; i < n; i++)
        {
            ThreadData td("thread" + std::to_string(i));
            _threads.emplace_back(td, std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1));
        }
    }
public:

    class GG
    {
    public:
        ~GG()
        {
            if(_p){
                delete _p;
                _p = nullptr;
            }
        }
    };

    ThreadPool(const ThreadPool& tmp) = delete;
    ThreadPool& operator=(const ThreadPool& tmp) = delete;

    static ThreadPool<T>* GetInstance()
    {
        if(_p == nullptr)
        {
            LockGuard lock(&mtx);
            if(_p == nullptr)
            {
                _p = new ThreadPool<T>(10);
            }
        }
        return _p;
    }


    void ThreadRun(ThreadData& td)
    {
        while (1)
        {
            T t;
            {
                LockGuard lock(&_mtx);
                while(_q.empty())
                {
                    pthread_cond_wait(&_cond, &_mtx);
                }
                t = _q.front(); _q.pop();
            }
            t();
        }
    }

    void Start()
    {
        for(auto& thread : _threads)
        {
            thread.Start();
        }
    }

    void push(const T& x)
    {
        LockGuard lock(&_mtx);
        _q.push(x);
        pthread_cond_signal(&_cond);
    }

    void Wait()
    {
        for(auto& thread: _threads)
        {
            thread.Join();
        }
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }
private:
    std::vector<Thread<ThreadData>> _threads;
    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
    std::queue<T> _q;
    static ThreadPool * _p;
    static pthread_mutex_t mtx;
    int _sockfd;
    static GG _g;
};
template<class T>
ThreadPool<T>* ThreadPool<T>::_p = nullptr;
template<class T>
pthread_mutex_t ThreadPool<T>::mtx = PTHREAD_MUTEX_INITIALIZER;
template<class T>
typename ThreadPool<T>::GG ThreadPool<T>::_g;